package com.google.code.virtualhockey.vhx.server.servlet.rest

import com.google.code.virtualhockey.vhx.server.servlet.VhxEnv
import com.google.code.virtualhockey.vhx.server.service.UserService
import com.google.code.virtualhockey.vhx.server.servlet.rest.error.{RestNotAuthorizedException, RestNotAuthenticatedException, RestSecurityTokenException}
import javax.servlet.http.Cookie
import java.util.logging.Logger

/**
 * Must be implemented by all concrete command handlers.
 */
trait RestCommandHandler {

  protected val LOG: Logger = Logger.getLogger( classOf[RestCommandHandler].getName )
  
  /**
   * Performs all necessary pre-checks and validations and then processes the given command by
   * calling {@link #handle(RestCommandRequest)} which is supposed to be implemented by the concrete
   * command handler implementation.
   *
   * @param cmd the command to be handled
   */
  private[rest] def process( cmd: RestCommandRequest ): String = {
    validateReferrer( cmd )
    validateUserAuthorization( cmd )
    handle( cmd )
  }

  private def getSessionCookie( cmd: RestCommandRequest ): Option[Cookie] = {
    if ( cmd.httpRequest.getCookies != null ) {
      val optTmp = cmd.httpRequest.getCookies.find( _.getName == VhxEnv.SESSION_COOKIE )
      if ( optTmp == None )
        cmd.httpRequest.getCookies.find( _.getName == VhxEnv.SECURE_SESSION_COOKIE )
      else
        optTmp
    }
    else
      None
  }

  /**
   * Validates the referrer header of the request.
   *
   * @param cmd the command to be handled
   * @throws RestSecurityTokenException if the security token validation fails
   */
  protected def validateReferrer( cmd: RestCommandRequest ) {
    // if we are running locally or no user is authenticated, let's assume everything is right
    //
    if ( VhxEnv.isDevelopmentServer || !UserService.isAuthenticated )
      return

    // we are running in production mode, check the referrer header
    //
    val strReferrer = cmd.httpRequest.getHeader( "referer" );
    LOG.info( "Received referrer: " + strReferrer ) // TODO remove log statement
    if ( strReferrer == null )
      throw new RestSecurityTokenException( "No 'referer' header is specified." )

    val strExpectedReferrer =
      if ( strReferrer.startsWith( "https" ) )
        "https://" + VhxEnv.REFERRER_URL
      else
        "http://" + VhxEnv.REFERRER_URL

    if ( strReferrer != strExpectedReferrer )
      throw new RestSecurityTokenException( "Referrer header validation failed." )
  }

  /**
   * Checks whether the currently connected user is allowed to execute this command handler. Please
   * override if this command needs a specific authorization verification.
   *
   * @param cmd the command to be handled
   * @throws RestNotAuthenticatedException if a user must be authenticated but is not
   * @throws RestNotAuthorizedException if a user is authenticated but does not have sufficient privileges
   */
  protected def validateUserAuthorization( cmd: RestCommandRequest ) {
    if ( requiredAuthRole == RestAuthRole.ANY )
      return
    else if ( requiredAuthRole == RestAuthRole.AUTHENTICATED )
      if ( !UserService.isAuthenticated )
        throw new RestNotAuthenticatedException( getClass.getName + " requires a user to be authenticated", UserService.getLoginUrl )
      else
        return
    else if ( requiredAuthRole == RestAuthRole.ADMIN )
      if ( !UserService.isAdmin )
        throw new RestNotAuthorizedException( UserService.getUserEmail + " is not authorized to access resource " + getClass.getName )
      else
        return
    else
      sys.error( "Unknown RestAuthRole: " + requiredAuthRole.toString )
  }

  /**
   * Handles the given command request. This method is responsible for performing the actual
   * work that has been requested. It is not responsible for doing any kind of prechecks (like
   * the validation of a security token or the validation of the current user).
   *
   * @param cmd the command to be handled
   * @return a response to be sent back to the client (must be a valid JSON string)
   * @throws RestCommandException signals abonormal termination of the command handling
   */
  protected def handle( cmd: RestCommandRequest ): String

  /**
   * Returns the role that the currently connected user must have to be able to execute the command.
   */
  protected val requiredAuthRole: RestAuthRole.Value
}
